import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  OnGatewayConnection,
  OnGatewayDisconnect,
  OnGatewayInit,
  MessageBody,
  ConnectedSocket,
} from '@nestjs/websockets';
import { Logger, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common';
import { Server, Socket } from 'socket.io';
import { ConfigService } from '@nestjs/config';
import { ThrottlerGuard } from '@nestjs/throttler';
import { WebsocketService } from './websocket.service';
import { RoomsService } from '../rooms/rooms.service';
import { RedisService } from '../redis/redis.service';
import {
  JoinRoomMessageDto,
  LeaveRoomMessageDto,
  MediaStateMessageDto,
  SignalingMessageDto,
  ChatMessageDto,
  NetworkQualityMessageDto,
} from './dto/websocket-message.dto';

@WebSocketGateway({
  cors: {
    origin: process.env.FRONTEND_URL || 'http://localhost:3000',
    credentials: true,
  },
  namespace: '/video-call',
  transports: ['websocket', 'polling'],
})
@UseGuards(ThrottlerGuard)
@UsePipes(new ValidationPipe({ transform: true }))
export class WebsocketGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer()
  server: Server;

  private readonly logger = new Logger(WebsocketGateway.name);

  constructor(
    private readonly websocketService: WebsocketService,
    private readonly roomsService: RoomsService,
    private readonly redisService: RedisService,
    private readonly configService: ConfigService,
  ) {}

  afterInit(server: Server) {
    this.logger.log('WebSocket服务器初始化完成');
    this.websocketService.setServer(server);
  }

  async handleConnection(client: Socket) {
    try {
      const userId = this.extractUserId(client);
      const roomId = this.extractRoomId(client);

      this.logger.log(
        `客户端连接: ${client.id}, 用户: ${userId}, 房间: ${roomId}`,
      );

      // 验证连接
      await this.websocketService.validateConnection(client, userId, roomId);

      // 存储连接信息
      await this.websocketService.storeConnection(client.id, userId, roomId);

      // 发送连接成功消息
      client.emit('connected', {
        socketId: client.id,
        userId,
        roomId,
        timestamp: new Date(),
      });
    } catch (error) {
      this.logger.error(`连接失败: ${error.message}`);
      client.emit('error', {
        type: 'CONNECTION_ERROR',
        message: error.message,
        timestamp: new Date(),
      });
      client.disconnect();
    }
  }

  async handleDisconnect(client: Socket) {
    try {
      const connectionInfo = await this.websocketService.getConnection(
        client.id,
      );

      if (connectionInfo) {
        this.logger.log(
          `客户端断开连接: ${client.id}, 用户: ${connectionInfo.userId}, 房间: ${connectionInfo.roomId}`,
        );

        // 处理断开连接
        await this.websocketService.handleDisconnection(client.id);

        // 通知房间内其他用户
        client.to(connectionInfo.roomId).emit('participant-disconnected', {
          userId: connectionInfo.userId,
          socketId: client.id,
          timestamp: new Date(),
        });
      }
    } catch (error) {
      this.logger.error(`处理断开连接失败: ${error.message}`);
    }
  }

  @SubscribeMessage('join-room')
  async handleJoinRoom(
    @MessageBody() data: JoinRoomMessageDto,
    @ConnectedSocket() client: Socket,
  ) {
    try {
      const result = await this.websocketService.handleJoinRoom(client, data);

      // 加入Socket.IO房间
      await client.join(data.roomId);

      // 通知房间内其他用户
      client.to(data.roomId).emit('participant-joined', {
        participant: result.participant,
        timestamp: new Date(),
      });

      // 发送房间信息给新用户
      client.emit('room-joined', {
        room: result.room,
        participant: result.participant,
        participants: result.participants,
        timestamp: new Date(),
      });

      this.logger.log(`用户 ${data.userId} 加入房间 ${data.roomId}`);
    } catch (error) {
      this.logger.error(`加入房间失败: ${error.message}`);
      client.emit('error', {
        type: 'JOIN_ROOM_ERROR',
        message: error.message,
        timestamp: new Date(),
      });
    }
  }

  @SubscribeMessage('leave-room')
  async handleLeaveRoom(
    @MessageBody() data: LeaveRoomMessageDto,
    @ConnectedSocket() client: Socket,
  ) {
    try {
      await this.websocketService.handleLeaveRoom(client, data);

      // 离开Socket.IO房间
      await client.leave(data.roomId);

      // 通知房间内其他用户
      client.to(data.roomId).emit('participant-left', {
        userId: data.userId,
        timestamp: new Date(),
      });

      client.emit('room-left', {
        roomId: data.roomId,
        timestamp: new Date(),
      });

      this.logger.log(`用户 ${data.userId} 离开房间 ${data.roomId}`);
    } catch (error) {
      this.logger.error(`离开房间失败: ${error.message}`);
      client.emit('error', {
        type: 'LEAVE_ROOM_ERROR',
        message: error.message,
        timestamp: new Date(),
      });
    }
  }

  @SubscribeMessage('media-state-change')
  async handleMediaStateChange(
    @MessageBody() data: MediaStateMessageDto,
    @ConnectedSocket() client: Socket,
  ) {
    try {
      await this.websocketService.handleMediaStateChange(client, data);

      // 广播给房间内其他用户
      client.to(data.roomId).emit('participant-media-changed', {
        userId: data.userId,
        audioEnabled: data.audioEnabled,
        videoEnabled: data.videoEnabled,
        screenShareEnabled: data.screenShareEnabled,
        timestamp: new Date(),
      });

      this.logger.log(
        `用户 ${data.userId} 媒体状态变更: audio=${data.audioEnabled}, video=${data.videoEnabled}`,
      );
    } catch (error) {
      this.logger.error(`媒体状态变更失败: ${error.message}`);
      client.emit('error', {
        type: 'MEDIA_STATE_ERROR',
        message: error.message,
        timestamp: new Date(),
      });
    }
  }

  @SubscribeMessage('signaling')
  async handleSignaling(
    @MessageBody() data: SignalingMessageDto,
    @ConnectedSocket() client: Socket,
  ) {
    try {
      // WebRTC信令处理
      const targetSocketId = await this.websocketService.getUserSocketId(
        data.roomId,
        data.targetUserId,
      );

      if (targetSocketId) {
        // 转发信令消息给目标用户
        this.server.to(targetSocketId).emit('signaling', {
          type: data.type,
          payload: data.payload,
          fromUserId: data.fromUserId,
          timestamp: new Date(),
        });

        this.logger.debug(
          `信令转发: ${data.type} from ${data.fromUserId} to ${data.targetUserId}`,
        );
      } else {
        client.emit('error', {
          type: 'SIGNALING_ERROR',
          message: '目标用户不在线',
          timestamp: new Date(),
        });
      }
    } catch (error) {
      this.logger.error(`信令处理失败: ${error.message}`);
      client.emit('error', {
        type: 'SIGNALING_ERROR',
        message: error.message,
        timestamp: new Date(),
      });
    }
  }

  @SubscribeMessage('chat-message')
  async handleChatMessage(
    @MessageBody() data: ChatMessageDto,
    @ConnectedSocket() client: Socket,
  ) {
    try {
      const message = await this.websocketService.handleChatMessage(
        client,
        data,
      );

      // 广播给房间内所有用户（包括发送者）
      this.server.to(data.roomId).emit('chat-message', {
        id: message.id,
        content: message.content,
        userId: data.userId,
        nickname: message.nickname,
        timestamp: message.timestamp,
        type: message.type,
      });

      this.logger.log(`聊天消息: ${data.userId} in ${data.roomId}`);
    } catch (error) {
      this.logger.error(`聊天消息处理失败: ${error.message}`);
      client.emit('error', {
        type: 'CHAT_ERROR',
        message: error.message,
        timestamp: new Date(),
      });
    }
  }

  @SubscribeMessage('network-quality')
  async handleNetworkQuality(
    @MessageBody() data: NetworkQualityMessageDto,
    @ConnectedSocket() client: Socket,
  ) {
    try {
      await this.websocketService.handleNetworkQuality(client, data);

      // 可选：广播网络质量信息给房间内其他用户
      client.to(data.roomId).emit('participant-network-quality', {
        userId: data.userId,
        quality: data.quality,
        rtt: data.rtt,
        packetLoss: data.packetLoss,
        timestamp: new Date(),
      });
    } catch (error) {
      this.logger.error(`网络质量更新失败: ${error.message}`);
    }
  }

  @SubscribeMessage('ping')
  handlePing(@ConnectedSocket() client: Socket) {
    client.emit('pong', { timestamp: new Date() });
  }

  @SubscribeMessage('get-room-participants')
  async handleGetRoomParticipants(
    @MessageBody() data: { roomId: string },
    @ConnectedSocket() client: Socket,
  ) {
    try {
      const participants = await this.websocketService.getRoomParticipants(
        data.roomId,
      );

      client.emit('room-participants', {
        roomId: data.roomId,
        participants,
        timestamp: new Date(),
      });
    } catch (error) {
      this.logger.error(`获取房间参与者失败: ${error.message}`);
      client.emit('error', {
        type: 'GET_PARTICIPANTS_ERROR',
        message: error.message,
        timestamp: new Date(),
      });
    }
  }

  // 私有辅助方法
  private extractUserId(client: Socket): string {
    const userId = client.handshake.query.userId as string;
    if (!userId) {
      throw new Error('缺少用户ID');
    }
    return userId;
  }

  private extractRoomId(client: Socket): string {
    const roomId = client.handshake.query.roomId as string;
    if (!roomId) {
      throw new Error('缺少房间ID');
    }
    return roomId;
  }

  // 公共方法供其他服务调用
  async broadcastToRoom(roomId: string, event: string, data: any) {
    this.server.to(roomId).emit(event, data);
  }

  async sendToUser(userId: string, event: string, data: any) {
    const socketId = await this.websocketService.getUserSocketId('', userId);
    if (socketId) {
      this.server.to(socketId).emit(event, data);
    }
  }

  async getRoomSockets(roomId: string): Promise<string[]> {
    const room = this.server.sockets.adapter.rooms.get(roomId);
    return room ? Array.from(room) : [];
  }

  async getConnectedUsersCount(): Promise<number> {
    return this.server.sockets.sockets.size;
  }
}
